package de.koller.aframe.gl.loader;

import de.koller.oceanFramework.OceanException;
import de.koller.oceanFramework.math.RVec3;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.StringTokenizer;
import org.lwjgl.opengl.GL11;

public class PLYModel {
	
	private boolean loaded = false;
	private final String file;
	
	private PlyState state = PlyState.Header;
	
	private int vIndex = 0;
	private RVec3[] verteces;
	private RVec3[] normals;
	private int fIndex = 0;
	private int[][] faces;
	
	/** not VBO supported! */
	public PLYModel( String file ) {
		this.file = file;
	}

	private void load() {
		if( loaded )
			return;
		
		ClassLoader cl = PLYModel.class.getClassLoader();
		InputStream in = cl.getResourceAsStream( file );
		BufferedReader inb = new BufferedReader( new InputStreamReader( in ) );
		
		try {
			String line = inb.readLine();
			while( line != null ) {
				handleLine( line );
				line = inb.readLine();
			}
		} catch( IOException ex ) {
			System.err.println( ex );
		}
		
		loaded = true;
	}
	
	private void handleLine( String line ) {
		if( state == PlyState.Header ) {
			checkHeader( line );
			return;
		} else if( state == PlyState.Vertex ) {
			inVerteces( line );
		} 
		if( state == PlyState.Face ) {
			inFaces( line );
			return;
		}
			
	}
	
	private void inFaces( String line ) {
		if( fIndex >= faces.length ) {
			state = PlyState.Ready;
			return;
		}
	
		StringTokenizer t = new StringTokenizer( line );
		if( Integer.parseInt( t.nextToken() ) != 3 )
			throw new OceanException( "only ply-formats with 3 verteces per face are supported!" );
		
		faces[fIndex][0] = Integer.parseInt( t.nextToken() );
		faces[fIndex][1] = Integer.parseInt( t.nextToken() );
		faces[fIndex][2] = Integer.parseInt( t.nextToken() );
		fIndex++;
	}
	
	private void inVerteces( String line ) {
		if( vIndex >= verteces.length ) {
			state = PlyState.Face;
			return;
		}
		
		StringTokenizer t = new StringTokenizer( line );
		verteces[vIndex] = new RVec3(	Float.parseFloat( t.nextToken() ),
										Float.parseFloat( t.nextToken() ),
										Float.parseFloat( t.nextToken() ) );
		normals[vIndex] = new RVec3(	Float.parseFloat( t.nextToken() ),
										Float.parseFloat( t.nextToken() ),
										Float.parseFloat( t.nextToken() ) );
		vIndex++;
	}
	
	private void checkHeader( String line ) {
		if( line.startsWith( "element vertex " ) ) {
			int n = Integer.parseInt( line.substring( 15 ) );
			verteces = new RVec3[n];
			normals = new RVec3[n];
			return;
		} else if( line.startsWith( "element face " ) ) {
			int n = Integer.parseInt( line.substring( 13 ) );
			faces = new int[ n ][3];
			return;
		} else if( line.startsWith( "end_header" ) ) {
			state = PlyState.Vertex;
			return;
		}	
	}
	
	public void render() {
		load();
		
		for( int i = 0; i < faces.length; i++ )
			renderPolygon( i );
	}
	
	private void renderPolygon( int faceIndex ) {
		GL11.glBegin( GL11.GL_TRIANGLES );
		
		for( int i = 0; i < 3; i++ ) 
			renderVertex( faces[faceIndex][i] );
		
		GL11.glEnd();
	}
	
	private void renderVertex( int vertexIndex ) {
		GL11.glVertex3f( verteces[vertexIndex].x, verteces[vertexIndex].z, verteces[vertexIndex].y );
		GL11.glNormal3f( normals[vertexIndex].x, normals[vertexIndex].z, normals[vertexIndex].y );
	}
	
	private enum PlyState {
		Header, Vertex, Face, Ready;
	}
}
